package com.wsoft.bpm.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.wsoft.bpm.constants.BpmConstant;
import com.wsoft.bpm.entity.ActProcessMappingEntity;
import com.wsoft.bpm.entity.ActWorkflowConfigEntity;
import com.wsoft.bpm.enums.ApproveStatus;
import com.wsoft.bpm.enums.DynamicApprovalType;
import com.wsoft.bpm.enums.ExecuteType;
import com.wsoft.bpm.mapper.ActProcessMapper;
import com.wsoft.bpm.mapper.BpmActivitiMapper;
import com.wsoft.bpm.query.node.TaskQuery;
import com.wsoft.bpm.service.*;
import com.wsoft.bpm.vo.ApprovalNodeDataVO;
import com.wsoft.bpm.vo.NextSequenceFlowVO;
import com.wsoft.constant.CommonConstant;
import com.wsoft.core.exception.Asserts;
import com.wsoft.core.utils.LoginUserUtil;
import com.wsoft.entity.BaseEntity;
import com.wsoft.vo.LoginUserVO;
import com.wsoft.vo.RoleVO;
import lombok.extern.log4j.Log4j2;
import org.activiti.bpmn.model.*;
import org.activiti.engine.*;
import org.activiti.engine.delegate.Expression;
import org.activiti.engine.delegate.VariableScope;
import org.activiti.engine.history.HistoricActivityInstance;
import org.activiti.engine.history.HistoricTaskInstance;
import org.activiti.engine.history.HistoricTaskInstanceQuery;
import org.activiti.engine.impl.cfg.ProcessEngineConfigurationImpl;
import org.activiti.engine.impl.el.ExpressionManager;
import org.activiti.engine.impl.identity.Authentication;
import org.activiti.engine.impl.persistence.entity.ExecutionEntity;
import org.activiti.engine.impl.persistence.entity.TaskEntity;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.Execution;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.IdentityLink;
import org.activiti.engine.task.Task;
import org.activiti.engine.task.TaskInfo;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;
import java.util.stream.Collectors;

import static com.wsoft.bpm.constants.BpmConstant.PROCESS_DEF_CANDIDATE;

/**
 * @Author zhonglj
 * @Date 2024-08-28 14:15
 */
@Service
@Log4j2
public class RunProcessServiceImpl implements RunProcessService {
    @Resource
    private RuntimeService runtimeService;
    @Resource
    private TaskService taskService;
    @Resource
    private RepositoryService repositoryService;
    @Resource
    private HistoryService historyService;
    @Resource
    private DynamicJumpService dynamicJumpService;
    @Resource
    private ActProcessMapper actProcessMapper;
    @Resource
    private IActProcessMappingService iActProcessMappingService;
    @Resource
    private ProcessEngine processEngine;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String startProcess(String processDefinitionKey, String tableName, String businessKey, Map map, String userId) {
        if (map == null) {
            map = new HashMap(16);
        }

        if (StrUtil.isBlank(tableName)) {
            Asserts.fail("[bpm.process.error.run.blank.tableName]");
        }

        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        if (userId == null) {
            userId = loginUserVO.getId().toString();
        }

        // 获取流程候选人参数
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery()
                .processDefinitionKey(processDefinitionKey)
                .latestVersion()
                .singleResult();

        if (processDefinition == null) {
            Asserts.fail("[bpm.process.error.run.noneDefinition]");
        }

        // 校验流程定义的启动候选人和候选组
        checkCandidate(userId, processDefinition, loginUserVO);

        // 校验businessKey唯一性
        long count = iActProcessMappingService.count(Wrappers.lambdaQuery(ActProcessMappingEntity.class)
                .ne(ActProcessMappingEntity::getTableName, tableName)
                .eq(ActProcessMappingEntity::getBusinessKey, businessKey)
        );
        if (count > 0) {
            Asserts.fail("[bpm.process.error.run.startProcess.businessKey.exist]");
        }

        // 设置默认参数
        map.putAll(getBpmDefaultMap());
        Authentication.setAuthenticatedUserId(userId);
        ProcessInstance processInstance = runtimeService.startProcessInstanceByKey(processDefinitionKey, businessKey, map);
        if (processInstance == null || processInstance.getId() == null) {
            Asserts.fail("[bpm.process.error.start.failed]");
        }

        // 录入流程-数据表对应关系
        ActProcessMappingEntity entity = new ActProcessMappingEntity();
        entity.setProcessKey(processDefinitionKey);
        entity.setProcessInstantId(processInstance.getId());
        entity.setTableName(tableName);
        entity.setStatusColumn(BpmConstant.DEFAULT_STATUS_COLUMN);
        entity.setBusinessKeyColumn(BpmConstant.DEFAULT_BUSINESS_KEY_COLUMN);
        entity.setBusinessKey(businessKey);
        iActProcessMappingService.save(entity);
        // 修改数据工作流状态==>审批中
        int update = ActivitiCallAble.updateApproveStatus(processInstance.getId(), businessKey, ApproveStatus.APPROVING);
        if (update <= 0) {
            Asserts.fail("[sys.msg.failed]");
        }

        return processInstance.getId();
    }

    private void checkCandidate(String userId, ProcessDefinition processDefinition, LoginUserVO loginUserVO) {
        List<IdentityLink> identityLinks = repositoryService.getIdentityLinksForProcessDefinition(processDefinition.getId());
        if (CollUtil.isNotEmpty(identityLinks)) {
            List<String> userIds = identityLinks.stream()
                    .filter(e -> PROCESS_DEF_CANDIDATE.equals(e.getType()) && e.getUserId() != null)
                    .map(IdentityLink::getUserId)
                    .distinct()
                    .collect(Collectors.toList());
            List<String> groupIds = identityLinks.stream()
                    .filter(e -> PROCESS_DEF_CANDIDATE.equals(e.getType()) && e.getGroupId() != null)
                    .map(IdentityLink::getGroupId)
                    .distinct()
                    .collect(Collectors.toList());

            int allowedCount = 0;
            // 判断候选人是否符合
            if (CollUtil.contains(userIds, userId)) {
                allowedCount++;
            }
            // 判断候选组是否符合
            for (String group : groupIds) {
                List<String> split = StrUtil.split(group, ":");
                if (CollUtil.isEmpty(split)) {
                    continue;
                }

                String executeType = split.get(0);
                String executeValue = split.get(1);
                if (StrUtil.equals(executeType, ExecuteType.CANDIDATE_ORG.getValue())) {
                    if (StrUtil.startWith(loginUserVO.getLoginOrgCode(), executeValue)) {
                        allowedCount++;
                    }

                } else if (StrUtil.equals(executeType, ExecuteType.CANDIDATE_ROLE.getValue())) {
                    List<RoleVO> roles = loginUserVO.getRoles();
                    if (CollUtil.isNotEmpty(roles)) {
                        Optional<RoleVO> roleMatch = roles.stream().filter(e -> StrUtil.equals(e.getCode(), executeValue)).findFirst();
                        if (roleMatch.isPresent()) {
                            allowedCount++;
                        }
                    }
                }
            }

            if (allowedCount == 0) {
                Asserts.fail("[bpm.process.error.run.nonePermission]");
            }
        }
    }

    /**
     * 默认参数生成
     *
     * @param
     * @return java.util.Map
     * @author zhonglj
     * @date 2024-10-12 9:10
     **/
    private Map getBpmDefaultMap() {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        return MapUtil.builder(new HashMap<>(16))
                .put("orgCode", loginUserVO.getLoginOrgCode())
                .put("userId", loginUserVO.getId())
                .build();
    }

    @Override
    public void agree(String taskId, String comment) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            Asserts.fail("[bpm.process.error.empty.task]");
        }

        //检查登录人是否有权限处理
        List<Task> tasks = todoList();
        // 登录人没有待办任务
        if (CollUtil.isEmpty(tasks) ||
                // 待办任务不包含当前taskId
                !tasks.stream().map(TaskInfo::getId).collect(Collectors.toList()).contains(taskId)) {
            Asserts.fail("[bpm.process.error.agree.task.auth]");
        }


        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        if (task.getAssignee() == null) {
            // 拾取任务
            taskService.claim(taskId, loginUserVO.getId().toString());
        }

        taskService.addComment(taskId, task.getProcessInstanceId(), comment);
        // 办理完成任务
        taskService.complete(taskId);
    }

    @Override
    public void backOne(String taskId, String comment) {
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        String processInstanceId = task.getProcessInstanceId();

        // 获取所有历史任务（按创建时间降序）
        List<HistoricTaskInstance> hisTaskList = historyService
                .createHistoricTaskInstanceQuery()
                .processInstanceId(processInstanceId)
                .orderByTaskCreateTime()
                .desc()
                .list();

        // 获取所有历史活动
        List<HistoricActivityInstance> hisActivityList = historyService
                .createHistoricActivityInstanceQuery()
                .processInstanceId(processInstanceId)
                .list();

        // 判断是否能够退回
        if (CollUtil.isEmpty(hisTaskList) || hisTaskList.size() == 1) {
            Asserts.fail("[bpm.process.error.backOne.empty]");
            return;
        }

        // 当前任务
        HistoricTaskInstance currentTask = hisTaskList.get(0);
        // 当前活动
        HistoricActivityInstance currentActivity = hisActivityList.stream().filter(e -> currentTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);

        // 排除连续退回导致的问题
        int currentIndex = 0;
        for (int i = 1; i < hisTaskList.size(); i++) {
            HistoricTaskInstance eachTask = hisTaskList.get(i);
            if (eachTask.getTaskDefinitionKey().equals(currentTask.getTaskDefinitionKey())) {
                currentIndex = i;
            }
        }

        // 前一个任务
        HistoricTaskInstance lastTask = hisTaskList.get(currentIndex + 1);
        // 前一个活动
        HistoricActivityInstance lastActivity = hisActivityList.stream().filter(e -> lastTask.getId().equals(e.getTaskId())).collect(Collectors.toList()).get(0);

        // 返回上一个节点
        dynamicJumpService.jumpTask(processInstanceId, currentActivity.getActivityId(), lastActivity.getActivityId(), comment);
    }

    @Override
    public void setAssign(String taskId, String assignee) {
        taskService.setAssignee(taskId, assignee);
    }

    @Override
    public List<Map> history(String processInstanceId) {
        return actProcessMapper.history(processInstanceId);
    }

    @Override
    public void rollback(String processInstanceId, String comment) {
        Task task = taskService.createTaskQuery().processInstanceId(processInstanceId).singleResult();
        ProcessDefinition processDefinition = repositoryService.createProcessDefinitionQuery().processDefinitionId(task.getProcessDefinitionId()).singleResult();

        taskService.addComment(task.getId(), processInstanceId, comment);
        runtimeService.deleteProcessInstance(processInstanceId, comment);

        //回调退回
        ActivitiCallAble callService = ActivitiCallAble.findCallService(processDefinition.getKey());
        if (callService != null) {
            callService.callBack(processDefinition.getKey(), processInstanceId, task.getBusinessKey());
        }
    }

    @Override
    public List<Task> todoList() {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        // 拼接角色
        List<String> groups = new ArrayList<>();
        List<RoleVO> roles = loginUserVO.getRoles();
        if (CollUtil.isNotEmpty(roles)) {
            groups.addAll(roles.stream()
                    .map(e -> ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ROLE.getValue(), e.getCode()))
                    .collect(Collectors.toList()));
        }

        // 拼接组织
        groups.add(ExecuteType.getCandidateGroups(ExecuteType.CANDIDATE_ORG.getValue(), loginUserVO.getLoginOrgCode()));

        return taskService.createTaskQuery()
                .taskCandidateOrAssigned(loginUserVO.getId().toString(), groups)
                .list();
    }

    @Override
    public List<HistoricTaskInstance> doneList() {
        LoginUserVO loginUserVO = LoginUserUtil.getUserInfo().getLoginUserVO();
        HistoricTaskInstanceQuery query = historyService.createHistoricTaskInstanceQuery()
                // 查询指定用户的任务
                .taskAssignee(loginUserVO.getId().toString())
                // 查询已完成的任务
                .finished()
                // 按任务结束时间排序
                .orderByHistoricTaskInstanceEndTime()
                // 降序排列
                .desc();

        return query.list();
    }

    @Override
    public void stopProcess(String processInstanceId) {
        ProcessInstance processInstance = runtimeService.createProcessInstanceQuery().processInstanceId(processInstanceId).singleResult();
        if (processInstance != null) {
            // 强制结束流程，通过删除理由来区分
            runtimeService.deleteProcessInstance(processInstanceId, BpmConstant.STOP_PROCESS_INSTANCE_FORCEFULLY);
        }
    }

    @Resource
    private IActWorkflowConfigService workflowConfigService;
    @Resource
    private IActProcessMappingService actProcessMappingService;
    @Resource
    private BpmActivitiMapper bpmActivitiMapper;

    @Override
    public ApprovalNodeDataVO getNodeData(String businessKey, String taskId) {
        ApprovalNodeDataVO respVO = new ApprovalNodeDataVO();
        Task task = taskService.createTaskQuery().taskId(taskId).singleResult();
        if (task == null) {
            Asserts.fail("[bpm.process.error.empty.task]");
        }

        // 获取业务主键
        ActProcessMappingEntity processMapping = actProcessMappingService.getOne(
                Wrappers.lambdaQuery(ActProcessMappingEntity.class)
                        .eq(ActProcessMappingEntity::getProcessInstantId, task.getProcessInstanceId())
        );
        if (processMapping != null) {
            String idColumn = bpmActivitiMapper.selectPrimaryKey(processMapping.getTableName(), "wsoft");
            if (StrUtil.isNotBlank(idColumn)) {
                // 查询指定表单主键
                String keyColumnVal = bpmActivitiMapper.getKeyColumnVal(
                        processMapping.getTableName(),
                        idColumn,
                        processMapping.getBusinessKeyColumn(),
                        processMapping.getBusinessKey());
                respVO.setId(keyColumnVal);
            }
        }

        // 获取表单操作
        ActWorkflowConfigEntity nodeConfig = workflowConfigService.getOne(
                Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                        .eq(ActWorkflowConfigEntity::getActId, task.getTaskDefinitionKey())
                        .eq(ActWorkflowConfigEntity::getProcDefId, task.getProcessDefinitionId())
                        .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
        );
        if (nodeConfig != null && StrUtil.isNotBlank(nodeConfig.getJson())) {
            TaskQuery taskQuery = JSONUtil.toBean(nodeConfig.getJson(), TaskQuery.class);
            if (taskQuery != null) {
                // 页面操作
                respVO.setOperations(taskQuery.getOperations());
                // 页面表单
                respVO.setFormProperties(taskQuery.getFormProperties());
            }
        }

        // 获取选择分支
        List<NextSequenceFlowVO> nextSequenceFlows = getNextSequenceFlows(task);
        respVO.setNextSequenceFlows(nextSequenceFlows);

        return respVO;
    }

    private List<NextSequenceFlowVO> getNextSequenceFlows(Task task) {
        BpmnModel bpmnModel = repositoryService.getBpmnModel(task.getProcessDefinitionId());
        String currentTaskId = task.getId();
        List<SequenceFlow> nextSequenceFlows = new ArrayList<>();

        // 获取当前任务的 FlowNode
        FlowNode currentTaskNode = (FlowNode) bpmnModel.getFlowElement(currentTaskId);

        if (currentTaskNode != null) {
            // 获取当前任务的所有出站 SequenceFlow
            List<SequenceFlow> outgoingFlows = currentTaskNode.getOutgoingFlows();

            for (SequenceFlow outgoingFlow : outgoingFlows) {
                FlowElement targetElement = outgoingFlow.getTargetFlowElement();

                if (targetElement instanceof Gateway) {
                    // 如果后面是网关，获取网关的所有出站 SequenceFlow
                    List<SequenceFlow> gatewayOutgoingFlows = ((FlowNode) targetElement).getOutgoingFlows();
                    nextSequenceFlows.addAll(gatewayOutgoingFlows);
                } else if (targetElement instanceof UserTask) {
                    // 如果后面是用户任务，直接获取当前节点紧接着的分支
                    nextSequenceFlows.add(outgoingFlow);
                }
            }
        }

        ProcessEngineConfigurationImpl config = (ProcessEngineConfigurationImpl) processEngine.getProcessEngineConfiguration();
        ExpressionManager expressionManager = config.getExpressionManager();

        List<SequenceFlow> allowedSequenceFlows = nextSequenceFlows.stream()
                .filter(flow -> validateSequenceFlow(flow.getConditionExpression(), task, expressionManager))
                .collect(Collectors.toList());

        if (CollUtil.isNotEmpty(allowedSequenceFlows)) {
            // 获取分支后面的用户任务
            List<NextSequenceFlowVO> sequenceFlowVos = allowedSequenceFlows.stream()
                    .filter(flow -> flow.getTargetFlowElement() != null && flow.getTargetFlowElement() instanceof UserTask)
                    .map(flow -> {
                        NextSequenceFlowVO vo = new NextSequenceFlowVO();
                        vo.setSequenceId(flow.getId());
                        vo.setSequenceName(flow.getName());
                        UserTask next = (UserTask) flow.getTargetFlowElement();
                        vo.setTaskActId(next.getId());

                        // 获取页面配置的选择分支类型
                        ActWorkflowConfigEntity nodeConfig = workflowConfigService.getOne(
                                Wrappers.lambdaQuery(ActWorkflowConfigEntity.class)
                                        .eq(ActWorkflowConfigEntity::getActId, next.getId())
                                        .eq(ActWorkflowConfigEntity::getProcDefId, task.getProcessDefinitionId())
                                        .eq(BaseEntity::getDeleted, CommonConstant.YESNO_NO)
                        );
                        if (nodeConfig != null && StrUtil.isNotBlank(nodeConfig.getJson())) {
                            TaskQuery taskQuery = JSONUtil.toBean(nodeConfig.getJson(), TaskQuery.class);
                            if (taskQuery != null) {
                                // 选择分支类型
                                vo.setBranchType(taskQuery.getBranchType());
                                // 是否自选人员
                                vo.setOptionalPersonnel(StrUtil.equals(taskQuery.getDynamicApprovalType(), DynamicApprovalType.OPTIONAL_PERSONNEL.getValue()));
                            }
                        }

                        return vo;
                    })
                    .collect(Collectors.toList());

            return sequenceFlowVos;
        }

        return null;
    }

    private Boolean validateSequenceFlow(String conditionExpression, Task task, ExpressionManager expressionManager) {
        // 创建表达式
        Expression expression = expressionManager.createExpression(conditionExpression);
        // 计算表达式的值
        VariableScope variableScope = getVariableScopeFromTask(task);
        if (variableScope != null) {
            Object value = expression.getValue(variableScope);
            return ObjectUtil.equal(value, true);
        } else {
            return false;
        }
    }

    /**
     * 根据任务获取VariableScope
     *
     * @param task
     * @return org.activiti.engine.delegate.VariableScope
     * @author zhonglj
     * @date 2024-10-19 16:59
     **/
    private VariableScope getVariableScopeFromTask(Task task) {
        // 获取任务的执行上下文
        if (task instanceof TaskEntity) {
            TaskEntity taskEntity = (TaskEntity) task;
            return taskEntity.getExecution();

        } else {
            // 获取任务所属的流程实例 ID
            String processInstanceId = task.getProcessInstanceId();
            // 获取流程实例的执行上下文
            Execution execution = runtimeService.createExecutionQuery()
                    .processInstanceId(processInstanceId)
                    .singleResult();
            if (execution instanceof ExecutionEntity) {
                return (ExecutionEntity) execution;

            } else {
                // 获取任务的执行上下文
                Object object = taskService.getVariable(task.getId(), "execution");
                if (object != null) {
                    return (ExecutionEntity) object;
                } else {
                    return null;
                }

            }
        }
    }
}
